package com.wyg.lambda.practical;

import com.wyg.lambda.entity.Book;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Lambda学习实战操作练习
 *
 *      1.Lambda表达式
 *      2.方法引用
 *      3.StreamAPI
 *
 *  重点理解：
 *
 *      Collectors 集合收集器
 *      Comparator 比较器
 *
 * @ClassName: StreamPractical
 * @Auther: WangYG
 * @Date: 2020-02-23 16:27:21
 * @Version: 1.0
 */
public class StreamPractical {

    /**
     * 功能描述: 模仿http-get请求参数，字符串转换为Map演示
     *
     *      重点理解：Collectors.toMap 获取一个指定类型的集合收集器
     *
     *      Stream.of()  数组转换流
     *      stream.map() 转换方法(可以转换为任意指定类型)
     *      Collectors.toMap() 生产一个Map集合收集器
     *      Stream.collect() 通过传入的集合收集器，转换为集合。
     *
     * @methodName: one
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 17:43:48
     */

    public void one(){
        //userQuery.do?id=123&code=7758&type=admin&token=A34kBhZ3zb9n48NS17G&key=user
        //模仿http-get请求参数，字符串转换为Map演示
        String getQueryStr = "id=123&code=7758&type=admin&token=A34kBhZ3zb9n48NS17G&key=user";

        //A.普通方式-拆解执行
        //1.将字符串转换为数组["id=123" , "code=7758" , "type=admin" ...]
        Stream<String> stream = Stream.of(getQueryStr.split("&"));

        //2.使用stream.map()将元素字符串流转换为字符串数组流
        //["id","123"],["code","7758"],["type","admin"]...
        Stream<String[]> arrayStream = stream.map(new Function<String, String[]>() {
            @Override
            public String[] apply(String s) {
                //处理成这种一个一个key-value的数组["id","123"],["code","7758"],["type","admin"]...  并返回
                String[] keyVals = s.split("=");
                return keyVals;
            }
        });

        /**
         * 将转换后的字符串数组流-全部收集到Map集合中,需要调用stream.collect()方法，
         *  ---<R, A> R collect(Collector<? super T, A, R> collector);
         * 而collect()方法，需要传入一个Collector收集器，所以下面创建一个Map收集器：
         *
         *  通过Collectors.toMap(keyVal,ValueVal)转换为Map集合收集器:
         *
         *  public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(
         *      Function<? super T, ? extends K> keyMapper,   -- 取Key的值
         *      Function<? super T, ? extends U> valueMapper) -- 取Value的值
         *  {......}
         *
         */
        //3.创建mapCollector收集器
        Collector<String[], ?, Map<String, Object>> mapCollector = Collectors.toMap(
                //传入处理key的匿名内部类
                new Function<String[], String>() {
                    @Override
                    public String apply(String[] strArray) {
                        //将数组[0]第一个元素作为Map的key返回
                        return strArray[0];
                    }
                },
                //传入处理Value的匿名内部类
                new Function<String[], Object>() {
                    @Override
                    public Object apply(String[] strArray) {
                        //将数组[1]第二个元素作为Map的value返回
                        return strArray[1];
                    }
                }
        );

        //4.将arrayStream流的元素通过mapCollector收集器，收集成Map集合。
        Map<String, Object> paramesMap = arrayStream.collect(mapCollector);
        System.out.println("通过普通方式将查询字符串转换为Map ：" + paramesMap);

        //B-使用Lambda表达式-方法引用和链式调用转换实例 - 简介和明了
        //1.将查询字符串按&分隔，并转换为Stream流
        Map<String, String> paramesMap2 = Stream.of(getQueryStr.split("&"))
                //2.将转换后的流里面的元素按照=进行拆分并转换为数组[key,value],[key,value]...
                .map(x -> x.split("="))
                //3.将转换后的key-value数组，通过collect转换为Map集合
                //4.Collectors.toMap(keyFun,valueFun) 集合收集器
                .collect(Collectors.toMap(array -> array[0], array -> array[1]));
        System.out.println("通过Lambda表达式-方法引用和链式调用转换将查询字符串转换为Map ：" + paramesMap2);

    }

    /**
     * 功能描述: Stream流操作自定义对象列表，获取列表中的ids，并进行拼接和转换
     *      实现仿SQL语句拼接ids功能
     *
     *      重点理解：Collectors.joining( "," , "(" , ")" ) 将元素拼接成字符串
     *
     *  <p>
     *      list.stream()
     *      stream.map(x -> String.valueOf(x.getId()))  int转为字符串
     *      stream.map(x -> "'" +x.getId()+ "'" )  int转为俩边带单引号的字符串 'num'
     *      stream.collect(Collectors.toList()) 将流元素装配到集合中
     *      Collectors.joining(",") 将元素按逗号拼接成字符串 a,b,c
     *      //实现仿SQL语句拼接ids功能
     *      Collectors.joining( "," , "(" , ")" ) 将元素按逗号拼接成字符串，并在首尾添加小括号 (a,b,c...)
     *
     *  </p>
     *
     * @methodName: two
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 18:18:45
     */
    public void two(){

        //获取对象列表的id，并进行拼接

        //1.获取对象id组装成list列表
        List<Integer> list = getBooks().stream().map(Book::getId).collect(Collectors.toList());
        System.out.println("获取对象id组装成list列表：" + list);

        //2.获取对象id组装成按逗号分隔的字符串1,2,3,...
        String ids = getBooks().stream().map(x -> String.valueOf(x.getId())).collect(Collectors.joining(","));
        System.out.println("获取对象id组装成按逗号分隔的字符串：" + ids);

        //3.获取对象id组装成按逗号分隔的字符串-两边添加括号(1,2,3,...)
        String ids2 = getBooks().stream().map(x -> String.valueOf(x.getId())).collect(Collectors.joining(",","(",")"));
        System.out.println("获取对象id组装成括号里面按【逗号】分隔的字符串：" + ids2);

        //4.获取对象id组装成按【单引号逗号】分隔的字符串-两边添加括号('1','2','3',...)
        //实现仿SQL语句拼接ids功能
        String sqlIds = getBooks().stream().map(x -> "'" +x.getId()+ "'").collect(Collectors.joining(",","(",")"));
        System.out.println("获取对象id组装成括号里面按【单引号逗号】分隔的字符串：" + sqlIds);
    }


    /**
     * 功能描述: StreamAPI元素去重复-演示
     *
     *      重点理解：distinct() 元素去重
     *               Collectors.toSet() 获得一个Set收集器
     *
     * <P>
     *          list.stream()  转换为stream
     *          stream.map(Book::getType) 转换获取对象的type属性
     *          stream.distinct() 将流中的元素去重复
     *          stream.collect()  将元素收集为集合
     *          Collectors.toList() 获得一个List收集器
     *          Collectors.toSet() 获得一个Set收集器
     * </P>
     * @methodName: three
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 18:31:21
     */
    public void three(){

        //获取对象中的所有类型的列表集合
        List<String> list = getBooks().stream().map(Book::getType).collect(Collectors.toList());
        System.out.println("获取对象中的所有类型的列表集合：" + list);

        //获取对象中的所有类型的列表集合 - distinct去重复
        List<String> list1 = getBooks().stream().map(Book::getType).distinct().collect(Collectors.toList());
        System.out.println("获取对象中的所有类型的列表集合 - distinct去重复：" + list1);

        //获取对象中的所有类型的列表集合 - Set去重复
        Set<String> set = getBooks().stream().map(Book::getType).collect(Collectors.toSet());
        System.out.println("获取对象中的所有类型的列表集合 - set集合去重复：" + set);
    }

    /**
     * 功能描述: StreamAPI元素【排序】演示
     *
     *
     *      重点理解：sorted(Comparator) stream元素排序
     *               Comparator.compare() Comparator比较器 -> compare()真正的比较方法实现
     *               Comparator.reversed() Comparator比较器 -> 颠倒排序，即倒序
     *               Comparator.thenComparing(new Comparator<T>) 比较器按多重条件排序，可以进行多次排序
     *
     *
     *     重点★★★★★
     *
     *               Comparator.comparing(Comparator) 比较器的 -> 链式调用方法，简化步骤。
     *               如：Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate).reversed())
     *
     * <P>
     *         1.list.stream()  转换为stream
     *
     *         //2.创建一个按book对象价格进行正序比较的 -> 比较器
     *         Comparator<Book> priceComparator = new Comparator<Book>() {
     *             //需要传入俩个参数，根据俩个参数比较
     *             @Override
     *             public int compare(Book b1, Book b2) {
     *                 //重写实现方法，使用Double.compare(d1,d2)比较大小
     *                 return Double.compare(b1.getPrice(), b2.getPrice());
     *             }
     *         };
     *
     *          3.stream.sorted(priceComparator) 使用比较器，对stream元素进行正序排序
     *          4.stream.sorted(priceComparator.reversed()); 使用比较器，对stream元素进行倒序排序
     *          5.stream.collect()  将元素收集为集合
     *          6.Collectors.toList() 获得一个List收集器
     *          7.stream.forEach() 循环输出元素
     *
     *         //8.使用Comparator.thenComparing(new Comparator<T>) 进行再次排序
     *         priceComparator.thenComparing(new Comparator<Book>() {
     *             @Override
     *             public int compare(Book o1, Book o2) {
     *                 //按倒序排序，后面的大于前面的则是正值往前排，否则就是负值往后排。
     *                 //比较：如果o2的发布时间 晚于(大于) o1的发布时间，则返回1，否则返回-1
     *                 return o2.getPublishDate().isAfter(o1.getPublishDate()) ? 1 : -1;
     *             }
     *         });
     *         Comparator.thenComparing(new Comparator<T>) 可以进行多次排序
     *
     *         //9. Comparator.comparing(Comparator) 比较器的 -> 链式调用方法，简化步骤。
     *         public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor)
     *
     *         如：Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate).reversed())
     *         Comparator.comparing(Comparator)
     *
     * </P>
     *
     * @methodName: four
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 19:08:51
     */
    public void four(){

        //A-按普通拆分方式将book对象的价格排序(默认正序)，并返回列表结果集。
        //1.将book列表转换为stream流
        Stream<Book> stream = getBooks().stream();

        /**
         * 将转换后的流,进行排序比较。需要调用stream.sorted()方法，
         *  ---Stream<T> sorted(Comparator<? super T> comparator);
         * sorted()方法，需要传入一个Comparator比较器，所以下面创建一个Comparator比较器，按价格比较：
         *
         *  创建一个比较器：
         *
         *         Comparator<Book> priceComparator = new Comparator<Object>() {
         *             //需要传入俩个参数，根据俩个参数比较
         *             @Override
         *             public int compare(Object b1, Object b2) {
         *                 //自定义比较方法，返回比较结果 1,-1,0
         *                 return 0;
         *             }
         *         };
         *
         * 注意：比较器的正序和倒序，默认既是正序，倒序时使用reversed()
         *
         *         正序：stream.sorted(priceComparator);
         *         倒序：stream.sorted(priceComparator.reversed());
         *
         */
        //2-价格比较器：创建一个按book对象价格进行正序比较的 -> 比较器
        Comparator<Book> priceComparator = new Comparator<Book>() {
            //需要传入俩个参数，根据俩个参数比较
            @Override
            public int compare(Book b1, Book b2) {
                //重写实现方法，使用Double.compare(d1,d2)比较大小
                return Double.compare(b1.getPrice(), b2.getPrice());
            }
        };

        //3.1正序排序(从低到高)，使用stream.sorted(priceComparator)将价格比较器传入，进行比较收集。
        Stream<Book> sortedStream = stream.sorted(priceComparator);

        //3.2 倒序排序(颠倒，从高到低)，使用比较器的Comparator.reversed()方法，将元素排序颠倒。
        //Stream<Book> sortedStream = stream.sorted(priceComparator.reversed());

        System.out.println();
        System.out.println("1**********************按普通拆分方式将book对象的价格排序(默认正序)，并返回列表结果集**********************");
        //4.将收集的元素，装配到List集合中，并通过forEach循环输出
        sortedStream.collect(Collectors.toList()).forEach(System.out::println);


        //B-使用Lambda表达式-方法引用和链式调用，按照book对象的价格排序(默认正序)
        System.out.println();
        System.out.println("2**********************使用Lambda表达式-方法引用和链式调用，按照book对象的价格排序(默认正序)**********************");
        getBooks().stream()
                .sorted((book1,book2) -> Double.compare(book1.getPrice(),book2.getPrice()))
                .collect(Collectors.toList())
                .forEach(System.out::println);

        //C-使用Lambda表达式-方法引用和链式调用，按照book对象的价格排序-使用reversed()倒序排序
        System.out.println();
        System.out.println("3**********************使用Lambda表达式-方法引用和链式调用，按照book对象的价格排序-使用reversed()倒序排序**********************");
        getBooks().stream()
                .sorted(priceComparator.reversed())
                .collect(Collectors.toList())
                .forEach(System.out::println);


        //D - 上面A-2中比较器先按价格排序完成之后，下面再次按照book属性的publishDate发布时间进行倒序排序
        //使用Comparator.thenComparing(new Comparator<T>) 进行再次排序
        Comparator<Book> dateComparator = priceComparator.thenComparing(new Comparator<Book>() {
            @Override
            public int compare(Book o1, Book o2) {
                //按倒序排序，后面的大于前面的则是正值往前排，否则就是负值往后排。
                //比较：如果o2的发布时间 晚于(大于) o1的发布时间，则返回1，否则返回-1
                return o2.getPublishDate().isAfter(o1.getPublishDate()) ? 1 : -1;
            }
        });
        System.out.println();
        System.out.println("4**********************使用Lambda表达式-Comparator.thenComparing()，先按Book属性的price正序再按照publishDate倒序排序**********************");
        //使用Lambda表达式-方法引用和链式调用，先按Book属性的price正序再按照publishDate倒序排序
        getBooks().stream()
                .sorted(dateComparator)
                .collect(Collectors.toList())
                .forEach(System.out::println);

        //E - 使用Comparator.comparing()简化上面的所有写法
        //public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor)
        System.out.println();
        System.out.println("5**********************使用Lambda表达式-方法引用和链式调用，Comparator.comparing()简化按Book属性的price正序排序**********************");
        getBooks().stream().sorted(Comparator.comparing(Book::getPrice)).forEach(System.out::println);

        System.out.println();
        System.out.println("6**********************使用Lambda表达式-方法引用和链式调用，Comparator.comparing()简化按Book属性的price倒序排序**********************");
        getBooks().stream().sorted(Comparator.comparing(Book::getPrice).reversed()).forEach(System.out::println);

        System.out.println();
        System.out.println("7**********************使用Lambda表达式-方法引用和链式调用，Comparator.comparing()简化先按Book属性的price正序再按照publishDate倒序排序**********************");
        getBooks().stream().sorted(Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate).reversed())).forEach(System.out::println);

    }

    /**
     * 功能描述: StreamAPI - 将对象列表，转换为Map列表
     *
     *  重点：Collectors.toMap(keyFun,valueFun)
     *
     *      Collector<T, ?, Map<K,U>> toMap(
     *          Function<? super T, ? extends K> keyMapper,  --key转换  obj -> obj.getId()
     *          Function<? super T, ? extends U> valueMapper --value转换 obj -> obj
     *      )
     *
     * @methodName: five
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 20:10:02
     */
    public void five(){

        Map<Integer, Book> bookMap = getBooks().stream().collect(Collectors.toMap(book -> book.getId(), book -> book));
        System.out.println("bookMap : " + bookMap);
        Map<Integer, Book> bookMap2 = getBooks().stream().collect(Collectors.toMap(Book::getId, book -> book));
        System.out.println("bookMap2 : " + bookMap2);
    }

    /**
     * 功能描述: 求所有book价格的平均值
     *
     *      重点：   stream.collect(Collectors.averagingDouble(double))
     *              Collectors.averagingDouble(Book::getPrice)
     *
     *      <R, A> R collect(Collector<? super T, A, R> collector);
     *      public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper)
     *
     * @methodName: six
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 22:19:46
     */
    public void six(){

        //求所有book价格的平均值
        Double avg = getBooks().stream().collect(Collectors.averagingDouble(Book::getPrice));
        System.out.println("所有book价格的平均值 avg ：" + avg);
    }

    /**
     * 功能描述: StreamAPI - 求集合中的最大值和最小值实例
     *
     *      重点：
     *          0. stream.collect(Collectors)  收集流中的元素
     *          1. Collectors.maxBy(Comparator.comparing())  根据比较器获取集合中最大的值
     *          2. Collectors.minBy(Comparator.comparing())  根据比较器获取集合中最小的值
     *          3. Comparator.comparing()   比较器-比较
     *          4. Comparator.comparing(Book::getPrice)
     *                       .thenComparing(Comparator.comparing(Book::getPublishDate))
     *
     *          //比较器链式调用  连续比较多个属性
     *          Comparator.comparing().thenComparing(Comparator)
     *
     *      public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
     *
     *      public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
     *
     * @methodName: seven
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 22:28:02
     */
    public void seven(){

        //获取售价最高的book对象
        //public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
        Optional<Book> maxPriceBook = getBooks().stream().collect(Collectors.maxBy(Comparator.comparing(Book::getPrice)));
        System.out.println("售价最高的book对象：" + maxPriceBook);

        //获取售价最低的book对象
        //public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
        Optional<Book> minPrice = getBooks().stream().collect(Collectors.minBy(Comparator.comparing(Book::getPrice)));
        System.out.println("售价最低的book对象：" + minPrice);

        //发布时间最晚(最新)的book对象
        Optional<Book> maxPublishDateBook = getBooks().stream().collect(Collectors.maxBy(Comparator.comparing(Book::getPublishDate)));
        System.out.println("发布时间最晚(最新)的book对象：" + maxPublishDateBook);

        //发布时间最早(最老)的book对象
        Optional<Book> minPublishDateBook = getBooks().stream().collect(Collectors.minBy(Comparator.comparing(Book::getPublishDate)));
        System.out.println("发布时间最早(最老)的book对象：" + minPublishDateBook);

        //售价最高的，发布时间最晚的book对象
        Optional<Book> maxPriceMaxDateBook = getBooks().stream().collect(Collectors.maxBy(Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate))));
        System.out.println("售价最高的，发布时间最晚的book对象：" + maxPriceMaxDateBook);
    }


    /**
     * 功能描述: StreamAPI - stream流分组统计实例
     *
     *      重点：
     *              1.按book中type属性分组
     *                  Collectors.groupingBy(Book::getType)
     *
     *                相关方法
     *                  //Collectors.groupingBy(Function<>)
     *                  //public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
     *
     *
     *              2.Map的forEach遍历key，value
     *                  map.forEach((key,value) -> {System.out.println(key,value);})
     *
     *                相关方法：
     *                  //default void forEach(BiConsumer<? super K, ? super V> action)
     *
     *              3.按book-类型type进行分组，并统计每个分组中的元素总个数
     *                  Collectors.groupingBy(Book::getType, Collectors.counting())
     *
     *                相关方法：
     *                  //Collectors.groupingBy(Function<>, Collectors.counting())
     *                  //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
     *
     *              4.按book-类型type进行分组，并统计每个分组中的价格总和
     *                  Collectors.groupingBy(Book::getType, Collectors.summingDouble(Book::getPrice))
     *
     *                相关方法：
     *                  //Collectors.groupingBy(Function<>, Collectors.summingDouble(property))
     *                  //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
     *
     *              5.按book-类型type进行分组，并按照价格统计每组价格平局值
     *                  Collectors.groupingBy(Book::getType, Collectors.averagingDouble(Book::getPrice))
     *
     *                相关方法：
     *                  //Collectors.groupingBy(Function<>, Collectors.averagingDouble(property))
     *                  //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
     *
     *              6.按book-类型type进行分组，并统计每个分组中价格最贵的book对象
     *                  Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPrice)))
     *
     *                相关方法：
     *                  //Collectors.groupingBy(Function<>, Collectors.maxBy(Comparator.comparing(property)))
     *                  //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
     *
     *              7.按book-类型type进行分组，并统计每个分组中价格最低的book对象
     *                  Collectors.groupingBy(Book::getType, Collectors.minBy(Comparator.comparing(Book::getPrice)))
     *
     *                相关方法：
     *                  //Collectors.groupingBy(Function<>, Collectors.minBy(Comparator.comparing(property)))
     *                  //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
     *
     *
     * @methodName: eight
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-23 23:40:23
     */
    public void eight(){


        //1.按book-类型type进行分组，并输出分组中的元素
        //Collectors.groupingBy(Function<>)
        //public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
        Map<String, List<Book>> map = getBooks().stream().collect(Collectors.groupingBy(Book::getType));
        map.forEach((key,value) -> {
            System.out.println("key : " + key);
            System.out.println(value);
            System.out.println("-----------------------------");
        });

        System.out.println();
        //2.按book-类型type进行分组，并统计每个分组中的元素总个数
        //Collectors.groupingBy(Function<>, Collectors.counting())
        //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
        //Collectors.groupingBy(Book::getType, Collectors.counting()) 按类型type分组，并对每个组进行count()汇总个数
        Map<String, Long> countMap = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.counting()));
        System.out.println("每组元素总个数：" + countMap);

        System.out.println();
        //3.按book-类型type进行分组，并统计每个分组中的价格总和
        //Collectors.groupingBy(Function<>, Collectors.summingDouble(property))
        //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
        //Collectors.groupingBy(Book::getType, Collectors.summingDouble(Book::getPrice)) 按类型type分组，并按照价格Price字段对每个组进行sum求和
        Map<String, Double> sumPriceBookGroup = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.summingDouble(Book::getPrice)));
        System.out.println("每组价格总和：" + sumPriceBookGroup);

        System.out.println();
        //4.按book-类型type进行分组，并按照价格统计每组价格平局值
        //Collectors.groupingBy(Function<>, Collectors.averagingDouble(property))
        //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
        //Collectors.groupingBy(Book::getType, Collectors.averagingDouble(Book::getPrice)) 按类型type分组，并按照价格Price字段统计每组价格平局值
        Map<String, Double> avgPriceBookGroup = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.averagingDouble(Book::getPrice)));
        System.out.println("每组价格平局值：" + avgPriceBookGroup);

        System.out.println();
        //5.按book-类型type进行分组，并统计每个分组中价格最贵的book对象
        //Collectors.groupingBy(Function<>, Collectors.maxBy(Comparator.comparing(property)))
        //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
        //Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPrice))) 按类型type分组，并按照价格Price字段统计每组价格最高的book对象
        Map<String, Optional<Book>> maxPriceBookGroup = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPrice))));
        maxPriceBookGroup.forEach((key,value) ->{
            System.out.println("[" + key+"] 最贵的book是：");
            System.out.println(value);
            System.out.println("-----------------------------");
        });

        System.out.println();
        //6.按book-类型type进行分组，并统计每个分组中价格最低的book对象
        //Collectors.groupingBy(Function<>, Collectors.minBy(Comparator.comparing(property)))
        //public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
        //Collectors.groupingBy(Book::getType, Collectors.minBy(Comparator.comparing(Book::getPrice))) 按类型type分组，并按照价格Price字段统计每组价格最低的book对象
        Map<String, Optional<Book>> minPriceBookGroup = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.minBy(Comparator.comparing(Book::getPrice))));
        minPriceBookGroup.forEach((key,value) ->{
            System.out.println("[" + key+"] 最低的book是：");
            System.out.println(value);
            System.out.println("-----------------------------");
        });

        System.out.println();
        //7.按book-类型type进行分组，并统计每个分组中出版时间最晚的book对象(类似上面)
        //Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPublishDate)))
        Map<String, Optional<Book>> maxPublishDateBookGourp = getBooks().stream().collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPublishDate))));
        minPriceBookGroup.forEach((key,value) ->{
            System.out.println("[" + key+"] 出版最新的book是：");
            System.out.println(value);
            System.out.println("-----------------------------");
        });
    }

    /**
     * 功能描述: StreamAPI - 使用filter过滤，筛选价格大于80，并且按出版时间最晚的排序。
     *
     * <P>
     *     重点：
     *          1. list转为stream流
     *              list.stream()
     *
     *          2. filter()过滤筛选方法
     *              stream.filter(book -> book.getPrice() >= 80)
     *
     *          3. sorted()排序方法【按照Comparator比较器进行排序】
     *              stream.sorted(Comparator.comparing(Book::getPublishDate).reversed())
     *
     *          4. Comparator.comparing 比较器简介写法，按照发布时间比较并进行倒序
     *              Comparator.comparing(Book::getPublishDate).reversed()
     *
     *          5. forEach() 流的遍历方法
     *              stream.forEach(System.out::println);
     *
     * </P>
     *
     * @methodName: nine
     * @params: []
     * @return: void
     * @exception:
     * @auther: WangYG
     * @date: 2020-02-24 00:04:07
     */
    public void nine(){

        //使用filter过滤，筛选价格大于80，并且按出版时间最晚的排序。
        //list转为stream流
        getBooks().stream()
                //过滤筛选价格大于80的book对象
                .filter(book -> book.getPrice() >= 80)
                //将筛选的结果按发布时间倒序排序
                .sorted(Comparator.comparing(Book::getPublishDate).reversed())
                //输出
                .forEach(System.out::println);
    }


    private List<Book> getBooks(){
        List<Book> books = new ArrayList<>(15);
        books.add(new Book(1,"java","编程语言",50D, LocalDate.parse("2015-02-14")));
        books.add(new Book(2,"tomcat","应用服务器",45.5D, LocalDate.parse("2011-03-09")));
        books.add(new Book(3,"mysql","数据库",35.8D, LocalDate.parse("2013-10-05")));
        books.add(new Book(4,"php","编程语言",27D, LocalDate.parse("2015-06-23")));
        books.add(new Book(5,"jetty","应用服务器",32D, LocalDate.parse("2017-08-19")));
        books.add(new Book(6,"python","编程语言",50D, LocalDate.parse("2014-05-24")));
        books.add(new Book(7,"C++","编程语言",65.9D, LocalDate.parse("2016-07-08")));
        books.add(new Book(8,"oracle","数据库",79D, LocalDate.parse("2018-01-27")));
        books.add(new Book(9,"redis","数据库",79D, LocalDate.parse("2019-11-15")));
        books.add(new Book(10,"mongoDB","数据库",80D, LocalDate.parse("2018-12-29")));
        books.add(new Book(11,"javaScript","编程语言",20.6D, LocalDate.parse("2010-08-19")));
        books.add(new Book(12,"Linux","操作系统",100D, LocalDate.parse("2017-04-09")));
        books.add(new Book(13,"Windows2003","操作系统",150D, LocalDate.parse("2019-05-15")));
        books.add(new Book(14,"Kubernetes","运维",165D, LocalDate.parse("2020-01-11")));
        books.add(new Book(15,"SpringCloud","技术框架",165D, LocalDate.parse("2020-02-23")));
        books.add(new Book(16,"Docker","运维",158D, LocalDate.parse("2019-07-23")));
        return books;
    }

}
